/**
 * 
 */
package helpers;

import interfaces.State;
import interfaces.Transition;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Random;

import exceptions.NotDeterministInitalStateException;
import exceptions.NotDeterministTransitionException;
import exceptions.UnknownInitialStateException;

import automaton.DFA;
import automaton.StateImpl;
import automaton.TransitionImpl;

/**
 * @author LE VAN LINH
 *
 */
public class AutomataGeneration {

	@SuppressWarnings("unchecked")
	public static DFA automaticGeneration(int numStates, int numLabels) throws UnknownInitialStateException, NotDeterministInitalStateException, NotDeterministTransitionException{
		int numTrans = randomTrans(numLabels,numStates);
		State initialState = new StateImpl("0",true,randomBoolean());
		
		// initial the list of label
		List<Character> listLabels = new ArrayList<Character>();
		char lb = 'a';
		while(listLabels.size() < numLabels){
			listLabels.add(lb);
			lb++;
		}
		
		List<Transition<String>> listTransitions = new ArrayList<Transition<String>>();
		List<State> listStates = new ArrayList<State>();
		
		int idState = 0;
		PriorityQueue<State> queue = new PriorityQueue<>();
		queue.add(initialState);
		State s1;
		while((idState + 1)<numStates){
			State s = queue.poll();
			listStates.add(s);
			for (Character letter : listLabels) {				
				if(idState < numStates - 1){
					idState++;
					s1 = new StateImpl(String.valueOf(idState),false,randomBoolean());
					Transition<String> t = new TransitionImpl<String>(s,s1,letter.toString());
					queue.add(s1);
					listTransitions.add(t);
				}
				else
					break;
			}
		}
		
		while(listTransitions.size() < numTrans && queue.size() != 0){
			State s2 = queue.poll();
			for (Character letter : listLabels) {
				State s3 = randomState(listStates); 
				Transition<String> t = new TransitionImpl<String>(s2,s3,letter.toString());
				if(listTransitions.size() < numTrans)
					listTransitions.add(t);
				else
					break;
			}
		}
		
		while(listTransitions.size() < numTrans){
			for (Character letter : listLabels) {
				State s2 = randomState(listStates);
				State s3 = randomState(listStates); 
				Transition<String> t = new TransitionImpl<String>(s2,s3,letter.toString());
				if(listTransitions.size() < numTrans && !getTrans(listTransitions, s2, letter))
					listTransitions.add(t);
			}
		}
		
		Transition<String>[] result = new TransitionImpl[listTransitions.size()];
		listTransitions.toArray(result);
		DFA dfamini = new DFA(result);
		while(queue.size()>0){
			State s2 = queue.poll();
			dfamini.addState(s2);
		}
		dfamini.setNumTrans(numTrans);
		return dfamini;
	}
	
	
	private static boolean getTrans(List<Transition<String>> listTrans,State source, Character letter){
		for (Transition<String> transition : listTrans) {
			if(transition.source().equals(source) 
					&& transition.label().equals(letter.toString()))
				return true;
		}
		return false;
	}
	

	private static State randomState(List<State> listStates) {
		Random rand = new Random();
		return listStates.get(rand.nextInt(listStates.size()));
	}

	/***
	 * Random a bool value
	 * @return
	 */
	private static boolean randomBoolean() {
		Random rand = new Random();
		return rand.nextBoolean();		
	}

	/***
	 * Random generation the number of transitions
	 * @param numLabels
	 * @param numStates
	 * @return
	 */
	private static int randomTrans(int numLabels, int numStates) {
		Random ran = new Random();
		int n = ran.nextInt(3);
		int result = 0;
		switch(n){
			case 0:
				result = numStates;
				break;
			case 1:
				result = (numLabels * numStates)/2;
				break;
			case 2:
				result = numLabels * numStates ;
				break;
			default:
				result = numStates;
				break;
		}
		return result;
	}
	
}
